using System;
using System.Collections.Generic;

using FlatRedBall;
using FlatRedBall.Math.Geometry;

using Microsoft.Xna.Framework;
using Microsoft.Devices;
using Microsoft.Xna.Framework.Audio;

using Xzipit.Utilities;
using Xzipit.Interfaces;
using Xzipit.Utilities.Gestures;
using System.Diagnostics;
using Xzipit.Utilities.Touch;

namespace Xzipit.GameObjects
{
    class Button : Obstacle, ITappable, IPullable
    {
        #region Fields

        private int _type;
        private Vector3 _startPos;
        private Vector3 _endPos;
        private Vector3 _direction;
        private Circle _touchCollision;
        private Circle _startCircle;

        private List<Sprite> _thread;
        private const float TEXTURE_HEIGHT = 1.24264073f;

        private SoundEffect _touchSound;

        // Keep the ContentManager for easy access:

        #endregion

        #region Properties
        // Override the Alpha and AlphaRate property of Obstacle
        public override float Alpha
        {
            get { return _sprite.Alpha; }
            set 
            {
                _sprite.Alpha = value;

                _tooltip.Alpha = value;
                foreach (Sprite s in _thread)
                {
                    s.Alpha = value;
                }
            }
        }

        public override float AlphaRate
        {
            get { return _sprite.AlphaRate; }
            set 
            {
                _sprite.AlphaRate = value;

                _tooltip.AlphaRate = value;

                foreach (Sprite s in _thread)
                {
                    s.AlphaRate = value;
                }
            }
        }
        #endregion

        #region Methods

        #region Constructors
        public Button(string contentManagerName)
        {
            // Set the default type. This constructor should only be used for testing.
            _contentManagerName = contentManagerName;
            _type = 1;

            Initialize(true);
        }

        public Button(string contentManagerName, float x, float y, int type, float x2, float y2)
        {
            // Sets parameters of a Button based on a level file.
            _contentManagerName = contentManagerName;
            _type = type;
            this.Position.X = x;
            this.Position.Y = y;
            this.Position.Z = 0.1f;
            _startPos = new Vector3(x, y, 0.0f);
            _endPos = new Vector3(x2, y2, 0.0f);

            _direction = _endPos - this.Position;
            _direction.Normalize();
            _direction *= 5;

            Initialize(true);
        }

        #endregion

        #region Initialization
        protected override void Initialize(bool addToManagers)
        {
            // Here you can preload any content you will be using
            // like .scnx files or texture files.

            if (addToManagers)
            {
                AddToManagers();
            }
        }

        private void InitializeThread()
        {
            _thread = new List<Sprite>();

            // calculate the distance between start and end nodes.
            float realLength = Math.Abs((_startPos - _endPos).Length() / TEXTURE_HEIGHT);
            int length = (int)realLength;

            // round up always so we get an integer number of segments.
            if (realLength - length > 0.0f)
            {
                length++;
            }

            // create a sprite and collision for all segments of the track.
            for (int i = 0; i < length; i++)
            {
                Sprite temp = SpriteManager.AddSprite(@"Content\GameObjects\Obstacles\button-thread", _contentManagerName);
                temp.AttachTo(_startCircle, false);
                float pixelsPerUnit = SpriteManager.Camera.PixelsPerUnitAt(temp.Z);
                temp.ScaleX = .5f * temp.Texture.Width / pixelsPerUnit;
                temp.ScaleY = .5f * temp.Texture.Height / pixelsPerUnit;
                temp.RelativeY = 0.0f - i * (2 * temp.ScaleY);
                temp.RelativeZ = -.05f;
                temp.Alpha = 0.0f;
                temp.UseAnimationRelativePosition = false;

                _thread.Add(temp);
            }

            // Rotates the thread so it is positioned correctly between 2 nodes.
            float faceAtX = _startPos.X - _endPos.X;
            float faceAtY = _startPos.Y - _endPos.Y;

            float rotation = (float)Math.Atan2(faceAtY, faceAtX) - (float)Math.PI / 2.0f;

            _startCircle.RotationZ = rotation;
        }

        public virtual void AddToManagers()
        {
            SpriteManager.AddPositionedObject(this);

            _sprite = SpriteManager.AddSprite(@"Content\GameObjects\Obstacles\button" + _type, _contentManagerName);

            // create a collision box for button touches. Is bigger than the sprite scale so the
            // player need not be as precise. Also accomodates for big fingers.
            _touchCollision = ShapeManager.AddCircle();
            _touchCollision.Radius = _sprite.ScaleX * 3.0f;
            _touchCollision.AttachTo(this, false);
            _touchCollision.Visible = false;

            _startCircle = ShapeManager.AddCircle();
            _startCircle.Radius = 0.0f;
            _startCircle.Position = this.Position;
            _startCircle.Visible = true;

            // The tooltip that comes on screen if it is a tutorial level.
            SetTooltip("tap");
            _tooltip.RelativeY = 1.0f;
            _tooltip.RelativeZ = 0.15f;

            // Displays the tutorial tooltip if it's a tutorial level
            InitializeThread();

            // Load the sound effect for tapping the button.
            _touchSound = FlatRedBallServices.Load<SoundEffect>(@"Content\Sound\button");

            TouchManager.AddTappable(this);
            base.AddToManagers(0.8f, 0.8f);
        }

        #endregion

        #region Update
        public override void Activity(GameGesture zipGesture, double t)
        {
            // tests for taps
            //if (_enabled)
            //{
            //    foreach (GameGesture g in GameGestureManager.Gestures)
            //    {
            //        if (g.Id != zipGesture.Id)
            //        {
            //            if (WasTapped(g))
            //            {
            //                OnTap();
            //                break;
            //            }
            //            //else if (WasPulled(g))
            //            //{
            //            //    OnPull();
            //            //    break;
            //            //}
            //        }
            //    }
            //}
            // Tests if the button has reached it's end point and reverses it's direction if so.
            //else 
                if (!_enabled && (this.Position - _endPos).Length() < 0.1f)
            {
                ReverseDirection();
            }

            // flashes the tutorial tooltip
            if (_tooltip.Visible)
            {
                PlayTooltip();
            }
        }

        public override void Collide(double t)
        {
            return;
        }

        #endregion

        #region Destroy
        public override void Destroy()
        {
            base.Destroy();

            ShapeManager.Remove(_touchCollision);
            ShapeManager.Remove(_startCircle);

            foreach (Sprite s in _thread)
            {
                SpriteManager.RemoveSprite(s);
            }

            TouchManager.RemoveTappable(this);
        }

        #endregion

        #region Private Methods

        #region Tooltip
        // Flashes the tap this indicator.
        protected override void PlayTooltip()
        {
            if (_tooltip.Alpha == 0.0f)
            {
                _tooltip.AlphaRate = 4.5f;
            }
            else if (_tooltip.Alpha == 1.0f)
            {
                _tooltip.AlphaRate = -1.0f;
            }
        }

        #endregion

        // Reverses the move to direction of the button once it reaches it's destination.
        private void ReverseDirection()
        {
            this.Velocity = Vector3.Zero;
            this.Position.Z = 0.1f;
            _sprite.Alpha = 1.0f;
            _endPos = _startPos;
            _startPos = this.Position;
            _direction = _endPos - this.Position;
            _direction.Normalize();
            _direction *= 5;
            _enabled = true;
        }

        #endregion

        #region Gesture Management

        #region Pull

        public bool WasPulled(GameGesture g)
        {
            return (g.GameGestureType == GameGestureType.Pull
                && _touchCollision.IsPointInside(g.End.X, g.End.Y)
                && _enabled);
        }

        public void OnPull(GameGesture g)
        {
            _enabled = false;
            GameProperties.Vibrate(.05);
            this.Velocity = _direction;
            _sprite.Alpha = 0.5f;
            _touchSound.PlaySound();
            _tooltip.Visible = false;
        }
        #endregion

        #region Tap

        public bool WasTapped(GameGesture g)
        {
            return (g.GameGestureType == GameGestureType.Tap
                && _touchCollision.IsPointInside(g.End.X, g.End.Y)
                && _enabled);
        }

        public void OnTap()
        {
            _enabled = false;
            GameProperties.Vibrate(.05);
            this.Velocity = _direction;
            this.Alpha = 0.5f;
            this.AlphaRate = 0.0f;
            _touchSound.PlaySound();
            _tooltip.Visible = false;
        }

        #endregion

        #endregion

        public override bool DoneFading()
        {
            bool result = base.DoneFading();

            foreach (Sprite s in _thread)
            {
                if (s.Alpha > 0.5f)
                {
                    s.Alpha = 0.5f;
                    s.AlphaRate = 0.0f;
                }
            }

            return result;
        }
        #endregion

    }
}
